package com.iogogogo.activity.aspect;

import com.iogogogo.activity.domain.dto.WebLogDTO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.commons.util.InetUtilsProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.PriorityOrdered;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * https://blog.csdn.net/u010675669/article/details/109010042
 * <p>
 * https://blog.csdn.net/yunxing323/article/details/108655250
 * <p>
 * Spring 5.2.0+的版本，建议你的配置类均采用Lite模式去做，即显示设置proxyBeanMethods = false。
 * Spring Boot在2.2.0版本（依赖于Spring 5.2.0）起就把它的所有的自动配置类的此属性改为了false，即@Configuration(proxyBeanMethods = false)，提高Spring启动速度
 * <p>
 * proxyBeanMethods = false ==> 告诉springboot这是一个配置类 == 配置文件
 * <p>
 * Created by tao.zeng on 2022/6/15.
 */
@Slf4j
@Aspect
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
@ConditionalOnProperty(prefix = "weblog", name = "enable", havingValue = "true", matchIfMissing = true)
public class WebLogAspect implements PriorityOrdered {

    private final InetUtils inetUtils;

    private final Executor executor;

    public WebLogAspect(InetUtilsProperties inetUtilsProperties, Executor executor) {
        inetUtils = new InetUtils(inetUtilsProperties);
        this.executor = executor;
    }

    @Pointcut("@annotation(com.iogogogo.activity.annotation.WebLog)")
    public void webLog() {
    }

    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        try {
            Object response = joinPoint.proceed();

            executor.execute(() -> {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                InetUtils.HostInfo hostInfo = inetUtils.findFirstNonLoopbackHostInfo();

                WebLogDTO webLogDTO = new WebLogDTO();
                webLogDTO.setClazz(joinPoint.getSignature().getDeclaringTypeName());
                webLogDTO.setMethod(joinPoint.getSignature().getName());

                Optional.ofNullable(joinPoint.getArgs())
                        .ifPresent(x -> webLogDTO.setArgs(Stream.of(x).collect(Collectors.toList())));

                Optional.ofNullable(attributes).ifPresent(x -> {
                    HttpServletRequest request = x.getRequest();
                    webLogDTO.setHostname(hostInfo.getHostname());
                    webLogDTO.setIpAddress(hostInfo.getIpAddress());
                    webLogDTO.setUrl(request.getRequestURL().toString());
                    webLogDTO.setHttpMethod(request.getMethod());
                });

                log.debug("\n\twebLog ===> {}\n\tresponse ===> {}", webLogDTO, response);
            });

            // 环绕通知后
            return response;
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public int getOrder() {
        // 保证事务等切面先执行
        return Integer.MAX_VALUE;
    }
}
